using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Net.Common;
using ProtoBuf;
using UnityEngine;

/****************************************************
// 功能：客户端请求网络 WebSocket
// | Talk To [ 聊来改善地区金融服务环境，助力企业发展 ]
// | Copyright (c) 2021-2025 聊来 All rights reserved.
// | Licensed ( <a href="http://www.apache.org/licenses/LICENSE-2.0">聊来</a> )
// | @Author: 贯彻己道的云 <18970881148@qq.com>
// | DateTime: 2025/4/15 11:33:43
*****************************************************/

public class WebSocketClient : SingletonPatternBase<WebSocketClient>
{

    private ClientWebSocket ws;
    private CancellationTokenSource cts;
    private int count;
    private readonly Queue<MsgInfo> delayMsgInfo = new Queue<MsgInfo>();
    public static event Action<MsgInfo> handleController;
    
    
    /// <summary>
    /// 连接WebSocket服务器
    /// </summary>
    public async void Connect()
    {
        cts = new CancellationTokenSource();
        count = 1;
        await ConnectWebSocket();
    }


    private async Task ConnectWebSocket()
    {
        try
        {
            ws = new ClientWebSocket();
            await ws.ConnectAsync(new Uri($"ws://{Constants.WEBSOCKET_ADDRESS}:{Constants.WEBSOCKET_PORT}"), cts.Token);

            // 启动消息接收线程
            ReceiveMessage();
            await StartHeartbeat();
        }
        catch (Exception e)
        {
            Debug.LogError($"WebSocket连接被拒绝，执行重连第{count}次，错误代码：{e.Message}");
            if (count < 5) { await Reconnect(); }
        }
    }

    private async void ReceiveMessage()
    {
        var buffer = new byte[4096];
        try
        {
            
            while (ws.State == WebSocketState.Open)
            {
                var result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), cts.Token);
                
                switch (result.MessageType)
                {
                    case WebSocketMessageType.Text:
                        var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
                        Debug.Log($"收到文本消息：{message}");
                        break;
                    case WebSocketMessageType.Binary:
                        // 解析长度前缀（大端转小端）
                        var length = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(buffer, 0));
                        var protobufData = new byte[length];
                        Array.Copy(buffer, 4, protobufData, 0, length);

                        // 反序列化
                        using (var stream = new MemoryStream(protobufData))
                        {
                            var msgInfo = Serializer.Deserialize<MsgInfo>(stream);
                            //Debug.Log($"收到二进制数据：{msgInfo}");
                            HandleTcpMsgInfo(msgInfo.requestType, msgInfo.data);
                        }
                        
                        break;
                    case WebSocketMessageType.Close:
                        await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "正常关闭", cts.Token);
                        break;
                    default:break;
                }
            } 
        }
        catch (Exception e)
        {
            Debug.LogError($"接收WebSocket发送的消息失败：{e.Message}");
            /*count = 0;
            await Reconnect();*/
        }
    }

    
    /// <summary>
    /// 重连
    /// </summary>
    private async Task Reconnect()
    {
        count++;
        await Task.Delay(TimeSpan.FromSeconds(1f));
        await ConnectWebSocket();
    }
    
    
    public void HandleTcpMsgInfo(RequestType type, string data)
    {
        lock (delayMsgInfo)
        {
            delayMsgInfo.Enqueue(new MsgInfo()
            {
                requestType = type,
                data = data
            });
        }
    }
    
    public void Dispatch()
    {
        lock (delayMsgInfo)
        {
            if (delayMsgInfo.Count <= 0) return;
            var list = delayMsgInfo.ToArray();
            delayMsgInfo.Clear();
            foreach (var msgInfo in list)
            {
                handleController?.Invoke(msgInfo);
            }
        }
    }

    
    /// <summary>
    /// 向WebSocket服务端发送消息
    /// 网络发送字节流是按大端序发送，也就是从左到右发送，和c#的小端序相反。
    /// 详细说明：https://www.cnblogs.com/cdaniu/p/15919284.html
    /// </summary>
    /// <param name="msg"></param>
    public async void Send(MsgInfo msg)
    {
        if (ws.State == WebSocketState.Open)
        {
            using var memory = new MemoryStream();
            Serializer.Serialize(memory, msg);
            var protobufData = memory.ToArray();
            
            // 添加长度前缀，本机到网络转换（使用 IPAddress.HostToNetworkOrder 确保长度前缀为大端字节序） java默认是大段字节序，c#在windows平台上默认是小字节序
            var lengthPrefix = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(protobufData.Length));
            var buffer = lengthPrefix.Concat(protobufData).ToArray();

            await ws.SendAsync(buffer, WebSocketMessageType.Binary, true, cts.Token);
        }
        else
        {
            Debug.LogError("向WebSocket服务端发送消息失效，执行从连操作");
            count = 0;
            await Reconnect();
        }
        
    }

    /// <summary>
    /// 开启心跳检测
    /// </summary>
    public async Task StartHeartbeat()
    {
        while (ws.State == WebSocketState.Open)
        {
            Send(new MsgInfo()
            {
                requestType = RequestType.WEBSOCKET_PING
            });
            await Task.Delay(TimeSpan.FromSeconds(10));
        }
    }
    
    
    /// <summary>
    /// 断开连接
    /// </summary>
    public async void Disconnect()
    {
        if (ws != null)
        {
            if (ws.State == WebSocketState.Open)
            {
                await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "关闭连接", cts.Token);
            }
            ws.Dispose();
        }
        
        Debug.LogError("非正常关闭应用 - WebSocket");
        

        cts.Cancel();
    }
    
}
